/*
 * Copyright 2010 Kevin L'Huillier <klhuillier@gmail.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.lhuillier.pwsafe.io.v3;


import org.lhuillier.pwsafe.io.RawDb;
import org.lhuillier.pwsafe.io.codec.BinConverter;
import org.lhuillier.pwsafe.io.codec.RawRecord;
import org.lhuillier.pwsafe.model.RawField;
import com.google.inject.Inject;


/**
 * After decryption, this performs the first step in handling the results.
 * It breaks the raw data into individual records, keeping the header and
 * list of records separate.
 */
class BinToRaw {
    private final BinConverter binDecode;

    @Inject
    BinToRaw(BinConverter conv) {
        this.binDecode = conv;
    }

    public RawDb read(byte[] data) {
        RawDb result = new RawDb();

        for (int i = 0; i < data.length;) {
            RawField field = rawField(data, i);
            i += blockLen(field);

            if (isEof(field)) {
                loadRecords(result, data, i);
                return result;
            } else {
                result.addHeader(field);
            }
        }

        throw new IllegalStateException("No End of Header found");
    }

    private RawField rawField(byte[] data, int offset) {
        int len = binDecode.readInt(data, offset);
        byte typeId = data[offset + 4];
        byte[] sliced = binDecode.slice(data, offset + 5, len);
        return new RawField(typeId, sliced);
    }

    private int blockLen(RawField field) {
        int length = 5 + field.getValue().length;
        return length + ((16 - (length % 16)) % 16);
    }

    private boolean isEof(RawField field) {
        return binDecode.byteToInt(field.getTypeId()) == 0xff;
    }

    private void loadRecords(RawDb db, byte[] data, int start) {
        RawRecord record = new RawRecord();

        for (int i = start; i < data.length;) {
            RawField field = rawField(data, i);
            i += blockLen(field);

            if (isEof(field)) {
                db.addRecord(record);
                record = new RawRecord();
            } else {
                record.addField(field);
            }
        }
    }
}
